/***********************************************************************************
**  
* @copyright (c) 2013-2100, ChengDu Westone Technology Co., LTD. All Right Reserved.
*
************************************************************************************/
/**
* @file	    duye_decoder.h
* @version     
* @brief      
* @author   
* @date	    2013-03-14
* @note 
*
*  1. 2017-03-14 Created this file
* 
*/
#include <duye_helper.h>
#include <duye_decoder.h>

namespace duye {

bool Charset::isUTF8(const uint8* buffer) {
	int32 i = 0;
	int32 n = 0;
	register uint8 c;
	bool ret = false;

	#define F 0  /* character never appears in text */
	#define T 1  /* character appears in plain ASCII text */
	#define I 2  /* character appears in ISO-8859 text */
	#define X 3  /* character appears in non-ISO extended ASCII (Mac, IBM PC) */

	static const uint8 textChars[256] = {
		/*            BEL BS HT LF  FF CR  */
		F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F, /* 0x0X */
		/*                    ESC      */
		F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F, /* 0x1X */
		T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x2X */
		T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x3X */
		T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x4X */
		T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x5X */
		T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x6X */
		T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F, /* 0x7X */
		/*        NEL                  */
		X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, /* 0x8X */
		X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x9X */
		I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xaX */
		I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xbX */
		I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xcX */
		I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xdX */
		I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xeX */
		I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I  /* 0xfX */
	};

	/* *ulen = 0; */
	for (i = 0; (c = buffer[i]); i++) {
		if ((c & 0x80) == 0) {
			/* 0xxxxxxx is plain ASCII */
			/*
			* Even if the whole file is valid UTF-8 sequences,
			* still reject it if it uses weird control characters.
			*/

			if (textChars[c] != T) {
				return false;
			}
		} else if ((c & 0x40) == 0) {
			/* 10xxxxxx never 1st byte */
			return false;
		} else {                  /* 11xxxxxx begins UTF-8 */
			int32 following;

			if ((c & 0x20) == 0) {        
				/* 110xxxxx */
				following = 1;
			} else if ((c & 0x10) == 0) {    
				/* 1110xxxx */
				following = 2;
			} else if ((c & 0x08) == 0) {    
				/* 11110xxx */
				following = 3;
			} else if ((c & 0x04) == 0) {   
				/* 111110xx */
				following = 4;
			} else if ((c & 0x02) == 0) {    
				/* 1111110x */
				following = 5;
			} else {
				return false;
			}

			for (n = 0; n < following; n++) {
				i ++;
				if (!(c = buffer[i])){
					goto end;
				}

				if ((c & 0x80) == 0 || (c & 0x40)) {
					return false;
				}
			}

			ret = true;
		}
	}

end:
	return ret;  /* don't claim it's UTF-8 if it's all 7-bit */
}

bool Charset::isGBK(const uint8* buffer, int bufferLen) {
	uint8 ch1;
	uint8 ch2;

	if (bufferLen >= 2)	{
		ch1 = *buffer;
		ch2 = *(buffer + 1);
		if (ch1 >= 129 && ch1 <= 254 && ch2 >= 64 && ch2 <= 254) {
			return true;
		}
	}

	return false;
}

void Charset::initDecode(Decode& decode) {
	for(int32 i = 0; i < MATRIX_SIZE; i++) {
		for(int32 j = 0; j < MATRIX_SIZE; j ++) {
			decode.map[i][j] = 0;
		}
	}

	uint8 code[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
	uint8 code2[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
	for(int32 i = 0; i < 16 ; i++) {
		for(int32 j = 0; j < 16; j++) {
			uint8 tmp,tmp1,tmp2;
			tmp1 = i << 4;
			tmp2 = j;
			tmp = tmp1 + tmp2;
			decode.map[code[i]][code[j]] = tmp;
			decode.map[code2[i]][code2[j]] = tmp;
		}
	}
}

int32 Charset::decode(const Decode& decode, uint8* buffer, int32 bufferLen) {
	uint8* begin = ByteHelper::findByte(buffer, bufferLen, '%');
	if (begin == NULL) {
		return bufferLen;
	}

	int32 write = 0;
	uint8* out = begin;
	int32 start = begin - buffer;
	int32 i = 0;

	for (i = start ; i < bufferLen - 5; i++) {
		if (buffer[i] == '%') {
			if ((buffer[i + 1] == 'u' || buffer[i + 1] == 'U') && 
				decode.map[buffer[i + 2]][buffer[i + 3]] != 0 && 
				decode.map[buffer[i + 4]][buffer[i + 5]] != 0) {
				out[write ++] = decode.map[buffer[i + 2]][buffer[i + 3]];
				out[write ++] = decode.map[buffer[i + 4]][buffer[i + 5]];
				i += 5;
			} else if(decode.map[buffer[i + 1]][buffer[i + 2]] != 0) {
				out[write ++] = decode.map[buffer[i + 1]][buffer[i + 2]];
				i += 2;
			}
		} else {
			out[write ++] = buffer[i];
		}
	}

	for(; i < bufferLen - 2; i++) {
		if(buffer[i] == '%' && decode.map[buffer[i + 1]][buffer[i + 2]] != 0){
			out[write] = decode.map[buffer[i + 1]][buffer[i + 2]];
			write ++;
			i += 2;
		} else {
			out[write ++] = buffer[i];
		}
	}
	out[write ++] = buffer[i ++];
	out[write ++] = buffer[i ++];
	out[write ++] = 0;
	return write + (begin - buffer);
}

}
